﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

namespace mbine.msgc.model
{
    public sealed class CConnection
    {
        public CMsgServer Server { get; set; }
        public bool IsConnect { get; set; }
        public string ErrorMessage { get; set; }

        public event EventHandler EventConnect;
        public event EventHandler EventClose;
        public event EventHandler EventDataReceive;
        public event EventHandler EventError;

        private CStateObject state;
        private Socket socket;

        public string Header { get; set; }
        public string SubHeader { get; set; }
        public string Body { get; set; }

        private int subHeaderLength = 0;
        private int bodyLength = 0;
        private int receiveSize = 0;

        static CConnection instance = null;
        static readonly object padlock = new object();

        CConnection()
        {
        }

        public static CConnection Instance
        {
            get
            {
                lock (padlock)
                {
                    if (instance == null)
                    {
                        instance = new CConnection();
                    }
                    return instance;
                }
            }
        }
        public void Connect()
        {
            try
            {
                //IPHostEntry ipHostInfo = Dns.GetHostEntry(Server.ServerName);
                //IPAddress ipAddress = ipHostInfo.AddressList[0];
                IPAddress ipAddress = IPAddress.Parse(Server.ServerName);
                IPEndPoint remoteEP = new IPEndPoint(ipAddress, Server.ServerPort);

                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), socket);
            }
            catch (Exception ex)
            {
                this.Body = ex.Message;
                if (EventError != null)
                    EventError(this, null);
            }
        }

        private void ConnectCallback(IAsyncResult _result)
        {
            try
            {
                Socket client = (Socket)_result.AsyncState;
                IsConnect = true;
                WaitForData();

                IsConnect = true;
                if (EventConnect != null)
                    EventConnect(this, null);
            }
            catch (Exception ex)
            {
                this.Body = ex.Message;
                if (EventError != null)
                    EventError(this, null);
            }
        }

        public void Send(string _s)
        {
            if (socket.Connected)
            {
                try
                {
                    byte[] byteData = Encoding.UTF8.GetBytes(_s);
                    socket.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), socket);
                }
                catch (Exception e)
                {
                    this.Body = e.Message;
                    if (EventError != null)
                        EventError(this, null);
                }
            }
        }

        private void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket client = (Socket)ar.AsyncState;
                int bytesSent = client.EndSend(ar);
            }
            catch (Exception e)
            {
                this.Body = e.Message;
                if (EventError != null)
                    EventError(this, null);
            }
        }

        public void WaitForData()
        {
            try
            {
                if (socket != null)
                {
                    Init();

                    state = new CStateObject();
                    state.buffer = new byte[28];
                    state.workSocket = socket;
                    socket.BeginReceive(state.buffer, 0, CStateObject.BufferSize, 0, new AsyncCallback(OnDataReceived), state);
                }
            }
            catch (SocketException se)
            {
                this.Body = se.Message;
                if (EventError != null)
                    EventError(this, null);
            }
        }

        private void OnDataReceived(IAsyncResult asyn)
        {
            try
            {
                CStateObject state = (CStateObject)asyn.AsyncState;
                Socket client = state.workSocket;

                int bytesRead = client.EndReceive(asyn);

                string tmp = Encoding.UTF8.GetString(state.buffer, 0, bytesRead);

                if (tmp != "")
                {
                    if (Header == null)
                    {
                        if (tmp.Substring(12, 8) != "" && tmp.Substring(20, 8) != "")
                        {
                            subHeaderLength = int.Parse(tmp.Substring(12, 8));
                            bodyLength = int.Parse(tmp.Substring(20, 8));
                            Header = tmp;

                            state.buffer = (subHeaderLength + bodyLength > 1024) ? new byte[1024]
                                : new byte[subHeaderLength + bodyLength];
                            asyn = client.BeginReceive(state.buffer
                                , 0
                                , state.buffer.Length
                                , 0
                                , new AsyncCallback(OnDataReceived)
                                , state);
                        }
                    }
                    else
                    {
                        Body += tmp;
                        receiveSize += bytesRead;
                        if (bodyLength + subHeaderLength > receiveSize)
                        {
                            state.buffer = (((bodyLength + subHeaderLength) - receiveSize) >= 1024) ? new byte[1024]
                                : new byte[(bodyLength + subHeaderLength) - receiveSize];

                            asyn = client.BeginReceive(state.buffer
                                , 0
                                , state.buffer.Length
                                , 0
                                , new AsyncCallback(OnDataReceived)
                                , state);
                        }
                        else
                        {
                            SubHeader = Body.Substring(0, subHeaderLength);
                            Body = Body.Substring(subHeaderLength, Body.Length - subHeaderLength);

                            if (EventDataReceive != null)
                                EventDataReceive(this, null);

                            WaitForData();
                        }
                    }
                }
            }
            catch (SocketException se)
            {
                this.Body = se.Message;
                if (EventError != null)
                    EventError(this, null);
            }
        }

        private void Init()
        {
            Header = null;
            SubHeader = null;
            Body = null;
            subHeaderLength = 0;
            bodyLength = 0;
            receiveSize = 0;
        }

        public void Close()
        {
            IsConnect = false;
            socket.Close();
            socket = null;
        }
    }

    public class CStateObject
    {
        public Socket workSocket = null;
        public const int BufferSize = 28;
        public byte[] buffer = new byte[BufferSize];
        public StringBuilder sb = new StringBuilder();
    }

    static class SocketExtensions
    {
        public static bool IsConnected(Socket socket)
        {
            try
            {
                return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
            }
            catch (SocketException) { return false; }
        }
    } 
}
